"""
聊天系统WebSocket服务 - 三端架构

一、客服端流程（/room/kefu{kefu_id}/）
1. 客服初始化连接：客服加入客服通知房间（kefu_room_{kefu_id}），接收新用户上线通知
2. 接收用户上线通知：当有用户发起咨询时，服务端向客服通知房间推送用户信息
3. 建立与用户的聊天连接：客服点击用户列表，通过 join_chat_room 加入聊天房间（chat_room_{user_id}）
4. 双向消息通信：客服在聊天房间中与用户进行实时消息交互
5. 断开连接：客服关闭页面时，从房间中移除连接

二、用户端流程（/room/user{user_id}/）
1. 发起在线咨询：用户连接，服务端为其创建聊天房间（chat_room_{user_id}），分配客服
2. 加载历史消息：从Redis中读取历史消息并推送给用户
3. 双向消息通信：用户在聊天房间中与客服进行实时消息交互
4. 断开连接：用户关闭页面时，从房间中移除连接

三、服务端流程
1. 连接处理：区分用户/客服，加入对应房间（通知房间或聊天房间）
2. 消息转发与存储：
   - 接收消息后，转发到对应的聊天房间，实现双向通信
   - 同时存入Redis（缓存，1天过期）和MySQL（持久化存储）
3. 断开处理：从房间中移除连接，释放资源

房间机制：
- 客服通知房间（kefu_room_X）：客服接收新用户上线通知和消息推送
- 聊天房间（chat_room_X）：用户和客服进行实际聊天的房间

消息存储：
- Redis：缓存最近1000条消息，1天过期，用于快速加载历史记录
- MySQL：持久化存储所有聊天记录，包含房间号、发送者类型、内容、时间等
"""
from fastapi import APIRouter, WebSocket, WebSocketDisconnect, Query
from typing import Dict, Set, Optional, List
import json
import asyncio
import logging
from datetime import datetime
from utils.redis_utils import get_redis
from utils.database import get_db, SessionLocal
from sqlalchemy.orm import Session
from Models.chat.models import ChatRoom, ChatMessage, MessageType
from Models.login.models import User

logger = logging.getLogger(__name__)

router = APIRouter()

# 商家列表（可以根据实际需求从数据库获取）
MERCHANT_LIST = [1001, 1002, 1003]  # 商家ID列表，用于负载均衡分配


class ChatRoomManager:
    """聊天房间管理器
    支持两种房间类型：
    1. 客服通知房间（kefu_room_X）：客服接收新用户上线通知
    2. 聊天房间（chat_room_X）：用户和客服进行实际聊天的房间
    """
    def __init__(self):
        self.active_rooms: Dict[str, Set[WebSocket]] = {}  # 房间ID为键，连接为值的字典
        self.websocket_info: Dict[WebSocket, tuple] = {}  # 连接为键，房间ID、用户ID、用户类型为值的字典
        self.kefu_connections: Dict[int, Set[WebSocket]] = {} # 客服ID为键，连接为值的字典
    
    async def connect(self, websocket: WebSocket, room_id: str, user_id: int, user_type: str):
        """建立连接并加入房间"""
        # 尝试 accept WebSocket，如果已经 accept 则忽略错误
        try:
            await websocket.accept()
        except RuntimeError as e:
            # 如果 WebSocket 已经 accept，会抛出 RuntimeError，忽略即可
            if "websocket.accept" not in str(e):
                raise
        
        if room_id not in self.active_rooms:
            self.active_rooms[room_id] = set()
        
        self.active_rooms[room_id].add(websocket)
        self.websocket_info[websocket] = (room_id, user_id, user_type)
        
        # 如果是客服，记录到客服连接集合中
        if user_type == "kefu":
            if user_id not in self.kefu_connections:
                self.kefu_connections[user_id] = set()
            self.kefu_connections[user_id].add(websocket)
        
        logger.info(f"用户 {user_id}({user_type}) 加入房间 {room_id}，当前房间连接数: {len(self.active_rooms[room_id])}")
    
    async def connect_to_chat_room(self, websocket: WebSocket, chat_room_id: str, user_id: int, user_type: str):
        """客服动态加入聊天房间（不重新accept，复用已有连接）"""
        if chat_room_id not in self.active_rooms:
            self.active_rooms[chat_room_id] = set()
        
        self.active_rooms[chat_room_id].add(websocket)
        # 更新websocket信息（客服可能同时在多个聊天房间）
        # 注意：这里不覆盖原有信息，因为客服需要在通知房间和聊天房间之间切换
        # 我们使用一个映射来记录客服的多个房间连接
        if websocket in self.websocket_info:
            # 如果已存在，记录到多个房间
            old_room_id, old_user_id, old_user_type = self.websocket_info[websocket]
            # 保持原有信息，但允许在同一websocket上监听多个房间
            pass
        
        logger.info(f"客服 {user_id} 加入聊天房间 {chat_room_id}，当前房间连接数: {len(self.active_rooms[chat_room_id])}")
    
    def disconnect_from_chat_room(self, websocket: WebSocket, chat_room_id: str):
        """客服退出聊天房间（但不断开WebSocket连接）"""
        if chat_room_id in self.active_rooms:
            self.active_rooms[chat_room_id].discard(websocket)
            if not self.active_rooms[chat_room_id]:
                del self.active_rooms[chat_room_id]
            logger.info(f"连接离开聊天房间 {chat_room_id}")
    
    def switch_room(self, websocket: WebSocket, new_room_id: str, user_id: int, user_type: str):
        """切换房间（不重新accept）"""
        # 先从旧房间移除
        if websocket in self.websocket_info:
            old_room_id, _, _ = self.websocket_info[websocket]
            if old_room_id in self.active_rooms:
                self.active_rooms[old_room_id].discard(websocket)
                logger.info(f"用户 {user_id}({user_type}) 离开房间 {old_room_id}，当前房间连接数: {len(self.active_rooms[old_room_id])}")
                if len(self.active_rooms[old_room_id]) == 0:
                    del self.active_rooms[old_room_id]
        
        # 加入新房间
        if new_room_id not in self.active_rooms:
            self.active_rooms[new_room_id] = set()
        
        self.active_rooms[new_room_id].add(websocket)
        self.websocket_info[websocket] = (new_room_id, user_id, user_type)
        
        logger.info(f"用户 {user_id}({user_type}) 切换到房间 {new_room_id}，当前房间连接数: {len(self.active_rooms[new_room_id])}")
    
    def disconnect(self, websocket: WebSocket):
        """断开连接"""
        if websocket in self.websocket_info:
            room_id, user_id, user_type = self.websocket_info[websocket]
            
            if room_id in self.active_rooms:
                self.active_rooms[room_id].discard(websocket)
                if not self.active_rooms[room_id]:
                    del self.active_rooms[room_id]
            
            # 如果是客服，从客服连接集合中移除
            if user_type == "kefu" and user_id in self.kefu_connections:
                self.kefu_connections[user_id].discard(websocket)
                if not self.kefu_connections[user_id]:
                    del self.kefu_connections[user_id]
            
            del self.websocket_info[websocket]
            logger.info(f"用户 {user_id}({user_type}) 离开房间 {room_id}")
    
    async def send_to_room(self, room_id: str, message: dict, exclude_websocket: Optional[WebSocket] = None):
        """向房间内所有连接发送消息
        
        返回成功发送的连接数量
        """
        if room_id not in self.active_rooms:
            return 0
        
        disconnected = set()
        sent_count = 0
        for websocket in self.active_rooms[room_id]:
            if websocket == exclude_websocket:
                continue
            try:
                await websocket.send_json(message)
                sent_count += 1
            except Exception as e:
                logger.error(f"发送消息到房间 {room_id} 失败: {e}")
                disconnected.add(websocket)
        
        # 清理断开的连接
        for ws in disconnected:
            self.disconnect(ws)
        
        return sent_count
    
    def get_room_users(self, room_id: str) -> list:
        """获取房间内在线用户信息"""
        if room_id not in self.active_rooms:
            return []
        
        users = []
        for ws in self.active_rooms[room_id]:
            if ws in self.websocket_info:
                _, user_id, user_type = self.websocket_info[ws]
                users.append({"user_id": user_id, "user_type": user_type})
        return users
    
    def get_kefu_websockets(self, kefu_id: int) -> Set[WebSocket]:
        """获取客服的所有WebSocket连接"""
        return self.kefu_connections.get(kefu_id, set())


# 全局房间管理器
room_manager = ChatRoomManager()


async def save_message_to_db(db: Session, room_id: str, sender_id: int, sender_type: str, 
                             content: str, message_type: MessageType = MessageType.TEXT):
    """保存消息到数据库"""
    try:
        # 验证必填字段
        if not room_id or not sender_id or not sender_type or not content:
            logger.error(f"保存消息失败: 必填字段缺失 - room_id={room_id}, sender_id={sender_id}, sender_type={sender_type}, content长度={len(content) if content else 0}")
            return None
        
        # 验证房间是否存在
        room = db.query(ChatRoom).filter(ChatRoom.room_id == room_id).first()
        if not room:
            logger.error(f"保存消息失败: 房间不存在 - room_id={room_id}")
            return None
        
        message = ChatMessage(
            room_id=room_id,
            sender_id=sender_id,
            sender_type=sender_type,
            content=content,
            message_type=message_type
        )
        db.add(message)
        db.commit()
        db.refresh(message)
        logger.debug(f"消息保存成功: id={message.id}, room_id={room_id}, sender_id={sender_id}, sender_type={sender_type}")
        return message
    except Exception as e:
        logger.error(f"保存消息到数据库失败: room_id={room_id}, sender_id={sender_id}, sender_type={sender_type}, error={str(e)}", exc_info=True)
        db.rollback()
        return None


def save_message_to_redis(room_id: str, message: dict):
    """保存消息到Redis，设置1天过期时间"""
    try:
        redis_client = get_redis()
        # 使用List存储历史消息，Key格式：chat_history:{room_id}
        redis_key = f"chat_history:{room_id}"
        message_json = json.dumps(message, ensure_ascii=False, default=str)
        redis_client.rpush(redis_key, message_json)
        # 设置过期时间为1天（86400秒）
        redis_client.expire(redis_key, 86400)
        # 限制列表最大长度为1000条，保留最新的1000条消息
        redis_client.ltrim(redis_key, -1000, -1)
        logger.debug(f"消息已保存到Redis: {room_id}")
    except Exception as e:
        logger.error(f"保存消息到Redis失败: {e}")


def get_redis_messages(room_id: str, limit: int = 50) -> List[dict]:
    """从Redis获取历史消息"""
    try:
        redis_client = get_redis()
        redis_key = f"chat_history:{room_id}"
        messages = redis_client.lrange(redis_key, -limit, -1)
        result = []
        for msg_json in messages:
            try:
                result.append(json.loads(msg_json))
            except:
                pass
        return result
    except Exception as e:
        logger.error(f"从Redis获取消息失败: {e}")
        return []


def assign_merchant(user_id: int) -> int:
    """分配商家（负载均衡）"""
    if not MERCHANT_LIST:
        return 1001  # 默认商家ID
    # 使用用户ID的哈希值进行负载均衡分配
    index = hash(str(user_id)) % len(MERCHANT_LIST)
    return MERCHANT_LIST[index]


# 客服WebSocket连接 - 一、客服初始化连接
# 参数：kefu_id - 客服ID，支持 kefu3 或 3 格式
@router.websocket("/room/kefu{kefu_id}/")
async def kefu_websocket(websocket: WebSocket, kefu_id: str):
    """
    客服WebSocket连接 - 一、客服初始化连接
    
    流程：
    1. 客服登录后，点击"回复消息"功能，前端初始化WebSocket连接
    2. 服务端识别参数kefu_id，将客服加入客服通知房间（kefu_room_{kefu_id}）
    3. 用于接收新用户上线通知和消息推送
    """
    try:
        # 解析客服ID（支持 kefu3 或 3 格式）
        kefu_id_int = int(kefu_id) if kefu_id.isdigit() else int(kefu_id.replace('kefu', '').replace('user', ''))
        
        # 客服通知房间：用于接收新用户上线通知
        kefu_notify_room = f"kefu_room_{kefu_id_int}"
        
        await room_manager.connect(websocket, kefu_notify_room, kefu_id_int, "kefu")
        db = SessionLocal()
        redis_client = get_redis()
        
        # 发送连接成功消息
        await websocket.send_json({
            "type": "connected",
            "room_id": kefu_notify_room,
            "message": "客服连接成功，等待接收用户上线通知"
        })
        
        # 获取在线用户列表（从Redis）
        online_users_key = f"kefu_online_users_{kefu_id_int}"
        online_user_ids = redis_client.smembers(online_users_key) or []
        
        # 如果有在线用户，发送用户列表
        if online_user_ids:
            user_list = []
            for chat_room_id_str in online_user_ids:
                try:
                    # chat_room_id_str 格式：chat_room_{user_id}
                    chat_room_id = chat_room_id_str.decode('utf-8') if isinstance(chat_room_id_str, bytes) else chat_room_id_str
                    room = db.query(ChatRoom).filter(ChatRoom.room_id == chat_room_id).first()
                    if room:
                        user = db.query(User).filter(User.id == room.user_id).first()
                        if user:
                            # 获取最后一条消息
                            last_message = db.query(ChatMessage).filter(
                                ChatMessage.room_id == chat_room_id
                            ).order_by(ChatMessage.created_at.desc()).first()
                            
                            user_info = {
                                "user_id": room.user_id,
                                "room_id": chat_room_id,
                                "username": user.username or f"用户{room.user_id}",
                                "nickname": user.nickname,
                                "last_message": None
                            }
                            
                            if last_message:
                                user_info["last_message"] = {
                                    "content": last_message.content[:50],
                                    "created_at": last_message.created_at.isoformat()
                                }
                            
                            user_list.append(user_info)
                except Exception as e:
                    logger.error(f"获取用户信息失败: {e}")
                    continue
            
            if user_list:
                await websocket.send_json({
                    "type": "user_list",
                    "data": user_list
                })
        
        # 监听消息
        while True:
            try:
                data = await websocket.receive_text()
                message_data = json.loads(data)
                
                action = message_data.get("action")
                
                if action == "join_chat_room":
                    # 三、建立与用户的聊天连接
                    # 客服点击用户列表中的用户，前端建立与用户房间号一致的WebSocket连接
                    chat_room_id = message_data.get("room_id")
                    if not chat_room_id:
                        await websocket.send_json({
                            "type": "error",
                            "message": "room_id不能为空"
                        })
                        continue
                    
                    # 验证房间是否存在
                    target_room = db.query(ChatRoom).filter(ChatRoom.room_id == chat_room_id).first()
                    if not target_room:
                        await websocket.send_json({
                            "type": "error",
                            "message": "房间不存在"
                        })
                        continue
                    
                    # 检查权限
                    kefu_user = db.query(User).filter(User.id == kefu_id_int).first()
                    is_platform_kefu = False
                    if kefu_user and kefu_user.role:
                        is_platform_kefu = kefu_user.role.code == "platform_kefu"
                    
                    if not is_platform_kefu and target_room.kefu_id != kefu_id_int:
                        await websocket.send_json({
                            "type": "error",
                            "message": "无权访问该房间"
                        })
                        continue
                    
                    # 客服加入聊天房间
                    await room_manager.connect_to_chat_room(websocket, chat_room_id, kefu_id_int, "kefu")
                    
                    # 如果房间还没有分配客服，自动分配当前客服
                    if not target_room.kefu_id:
                        target_room.kefu_id = kefu_id_int
                        db.commit()
                        logger.info(f"自动分配客服 {kefu_id_int} 到房间 {chat_room_id}")
                    
                    # 加载历史消息（从Redis）
                    history_messages = get_redis_messages(chat_room_id, limit=50)
                    if history_messages:
                        for msg in history_messages:
                            await websocket.send_json({
                                "type": "message",
                                "data": msg
                            })
                    
                    await websocket.send_json({
                        "type": "joined_chat_room",
                        "room_id": chat_room_id,
                        "message": f"已加入聊天房间 {chat_room_id}"
                    })
                    logger.info(f"客服 {kefu_id_int} 加入聊天房间 {chat_room_id}")
                
                elif action == "leave_chat_room":
                    # 客服退出聊天房间
                    chat_room_id = message_data.get("room_id")
                    if chat_room_id:
                        room_manager.disconnect_from_chat_room(websocket, chat_room_id)
                        await websocket.send_json({
                            "type": "left_chat_room",
                            "room_id": chat_room_id,
                            "message": f"已退出聊天房间 {chat_room_id}"
                        })
                
                elif action == "send_message":
                    # 四、双向消息通信 - 客服发送消息
                    content = message_data.get("content", "")
                    chat_room_id = message_data.get("room_id")
                    
                    if not chat_room_id or not content:
                        await websocket.send_json({
                            "type": "error",
                            "message": "room_id和content不能为空"
                        })
                        continue
                    
                    # 检查房间是否存在
                    target_room = db.query(ChatRoom).filter(ChatRoom.room_id == chat_room_id).first()
                    if not target_room:
                        await websocket.send_json({
                            "type": "error",
                            "message": "房间不存在"
                        })
                        continue
                    
                    # 检查权限
                    kefu_user = db.query(User).filter(User.id == kefu_id_int).first()
                    is_platform_kefu = False
                    if kefu_user and kefu_user.role:
                        is_platform_kefu = kefu_user.role.code == "platform_kefu"
                    
                    if not is_platform_kefu and target_room.kefu_id != kefu_id_int:
                        await websocket.send_json({
                            "type": "error",
                            "message": "无权访问该房间"
                        })
                        continue
                    
                    # 保存到数据库
                    try:
                        message = await save_message_to_db(db, chat_room_id, kefu_id_int, "kefu", content)
                        
                        if message:
                            message_dict = {
                                "id": message.id,
                                "room_id": str(message.room_id),
                                "sender_id": message.sender_id,
                                "sender_type": "kefu",
                                "content": message.content,
                                "created_at": message.created_at.isoformat()
                            }
                            
                            # 保存到Redis（1天过期）
                            save_message_to_redis(chat_room_id, message_dict)
                            
                            # 更新房间更新时间
                            target_room.updated_at = datetime.now()
                            db.commit()
                            
                            # 先发送消息给发送者本人（确认发送成功）
                            await websocket.send_json({
                                "type": "message",
                                "data": message_dict
                            })
                            
                            # 发送到用户聊天房间（用户可以看到）
                            # 用户连接时已经加入聊天房间，所以应该能收到消息
                            sent_count = await room_manager.send_to_room(
                                chat_room_id,
                                {
                                    "type": "message",
                                    "data": message_dict
                                },
                                exclude_websocket=websocket
                            )
                            
                            # 记录推送结果
                            if sent_count > 0:
                                logger.info(f"客服 {kefu_id_int} 发送消息到房间 {chat_room_id}，已推送给 {sent_count} 个用户: {content[:50]}")
                            else:
                                # 用户可能不在线，但消息已保存到数据库和Redis，用户重连时会加载
                                logger.info(f"客服 {kefu_id_int} 发送消息到房间 {chat_room_id}，用户当前不在线，消息已保存: {content[:50]}")
                        else:
                            # 保存失败
                            logger.error(f"客服 {kefu_id_int} 发送消息失败: 保存到数据库返回None, room_id={chat_room_id}")
                            await websocket.send_json({
                                "type": "error",
                                "message": "消息保存失败，请稍后重试"
                            })
                    except Exception as e:
                        # 捕获异常
                        logger.error(f"客服 {kefu_id_int} 发送消息异常: {str(e)}, room_id={chat_room_id}", exc_info=True)
                        db.rollback()
                        await websocket.send_json({
                            "type": "error",
                            "message": f"消息发送失败: {str(e)}"
                        })
                
                elif action == "get_user_list":
                    # 重新获取用户列表
                    online_user_ids = redis_client.smembers(online_users_key) or []
                    user_list = []
                    for chat_room_id_str in online_user_ids:
                        try:
                            chat_room_id = chat_room_id_str.decode('utf-8') if isinstance(chat_room_id_str, bytes) else chat_room_id_str
                            room = db.query(ChatRoom).filter(ChatRoom.room_id == chat_room_id).first()
                            if room:
                                user = db.query(User).filter(User.id == room.user_id).first()
                                if user:
                                    last_message = db.query(ChatMessage).filter(
                                        ChatMessage.room_id == chat_room_id
                                    ).order_by(ChatMessage.created_at.desc()).first()
                                    
                                    user_info = {
                                        "user_id": room.user_id,
                                        "room_id": chat_room_id,
                                        "username": user.username or f"用户{room.user_id}",
                                        "nickname": user.nickname,
                                        "last_message": None
                                    }
                                    
                                    if last_message:
                                        user_info["last_message"] = {
                                            "content": last_message.content[:50],
                                            "created_at": last_message.created_at.isoformat()
                                        }
                                    
                                    user_list.append(user_info)
                        except Exception as e:
                            logger.error(f"获取用户信息失败: {e}")
                            continue
                    
                    await websocket.send_json({
                        "type": "user_list",
                        "data": user_list
                    })
                
                elif action == "delete_room":
                    # 删除房间（仅平台客服可用）
                    chat_room_id = message_data.get("room_id")
                    if not chat_room_id:
                        await websocket.send_json({
                            "type": "error",
                            "message": "room_id不能为空"
                        })
                        continue
                    
                    # 检查权限：只有平台客服可以删除房间
                    kefu_user = db.query(User).filter(User.id == kefu_id_int).first()
                    is_platform_kefu = False
                    if kefu_user and kefu_user.role:
                        is_platform_kefu = kefu_user.role.code == "platform_kefu"
                    
                    if not is_platform_kefu:
                        await websocket.send_json({
                            "type": "error",
                            "message": "只有平台客服可以删除房间"
                        })
                        continue
                    
                    # 验证房间是否存在
                    target_room = db.query(ChatRoom).filter(ChatRoom.room_id == chat_room_id).first()
                    if not target_room:
                        await websocket.send_json({
                            "type": "error",
                            "message": "房间不存在"
                        })
                        continue
                    
                    # 软删除：将is_active设置为False
                    target_room.is_active = False
                    db.commit()
                    
                    # 从房间管理器中移除所有连接
                    if chat_room_id in room_manager.active_rooms:
                        # 获取该房间的所有连接
                        room_connections = list(room_manager.active_rooms[chat_room_id])
                        # 断开所有连接（但不断开WebSocket，只是从房间移除）
                        for ws in room_connections:
                            room_manager.disconnect_from_chat_room(ws, chat_room_id)
                    
                    # 获取所有相关客服列表（用于清理Redis和通知）
                    from Models.login.models import Role
                    platform_kefu_role = db.query(Role).filter(Role.code == "platform_kefu").first()
                    kefu_list = []
                    if platform_kefu_role:
                        kefu_users = db.query(User).filter(User.role_id == platform_kefu_role.id).all()
                        kefu_list = [kefu.id for kefu in kefu_users]
                    else:
                        kefu_role = db.query(Role).filter(Role.code == "kefu").first()
                        if kefu_role:
                            kefu_users = db.query(User).filter(User.role_id == kefu_role.id).all()
                            kefu_list = [kefu.id for kefu in kefu_users]
                    
                    # 如果房间有分配的客服，也从该客服的列表中移除
                    if target_room.kefu_id:
                        kefu_list.append(target_room.kefu_id)
                    
                    # 清理Redis缓存
                    try:
                        redis_client = get_redis()
                        # 删除历史消息缓存
                        redis_key = f"chat_history:{chat_room_id}"
                        redis_client.delete(redis_key)
                        
                        # 从所有相关客服的在线用户列表中移除
                        for kefu_id in set(kefu_list):
                            online_users_key = f"kefu_online_users_{kefu_id}"
                            redis_client.srem(online_users_key, chat_room_id)
                        
                        logger.info(f"平台客服 {kefu_id_int} 通过WebSocket删除房间 {chat_room_id}，已清理Redis缓存")
                    except Exception as e:
                        logger.error(f"清理Redis缓存失败: {e}")
                    
                    # 通知所有相关客服房间已删除
                    for kefu_id in set(kefu_list):
                        kefu_notify_room = f"kefu_room_{kefu_id}"
                        await room_manager.send_to_room(
                            kefu_notify_room,
                            {
                                "type": "room_deleted",
                                "data": {
                                    "room_id": chat_room_id,
                                    "deleted_by": kefu_id_int
                                }
                            }
                        )
                    
                    # 通知房间内的所有用户房间已删除
                    if chat_room_id in room_manager.active_rooms:
                        await room_manager.send_to_room(
                            chat_room_id,
                            {
                                "type": "room_deleted",
                                "data": {
                                    "room_id": chat_room_id,
                                    "message": "房间已被客服删除"
                                }
                            }
                        )
                    
                    await websocket.send_json({
                        "type": "room_deleted",
                        "room_id": chat_room_id,
                        "message": "房间已删除"
                    })
                    logger.info(f"平台客服 {kefu_id_int} 删除房间 {chat_room_id}")
                        
            except json.JSONDecodeError:
                await websocket.send_json({
                    "type": "error",
                    "message": "消息格式错误"
                })
            except Exception as e:
                logger.error(f"处理客服消息时出错: {e}", exc_info=True)
                await websocket.send_json({
                    "type": "error",
                    "message": f"处理消息时出错: {str(e)}"
                })
    
    except WebSocketDisconnect:
        # 五、断开连接
        room_manager.disconnect(websocket)
        logger.info(f"客服 {kefu_id} WebSocket断开")
    except Exception as e:
        logger.error(f"客服WebSocket错误: {e}", exc_info=True)
        room_manager.disconnect(websocket)
    finally:
        if 'db' in locals():
            db.close()



##用户
@router.websocket("/room/user{user_id}/")
async def user_websocket(websocket: WebSocket, user_id: str):
    """
    用户WebSocket连接 - 二、用户端流程
    
    流程：
    1. 用户点击首页"在线咨询"按钮，前端初始化WebSocket连接
    2. 服务端识别参数user_id，为其分配客服，创建聊天房间（chat_room_{user_id}）
    3. 将用户加入该房间，并向客服的通知房间推送用户上线消息
    """
    try:
        # 解析用户ID（支持 user3 或 3 格式）
        user_id_int = int(user_id) if user_id.isdigit() else int(user_id.replace('user', '').replace('kefu', '').replace('merchant', ''))
        
        db = SessionLocal()
        redis_client = get_redis()
        
        # 聊天房间ID格式：chat_room_{user_id}
        chat_room_id = f"chat_room_{user_id_int}"
        
        # 查询用户是否有已存在的房间
        chat_room = db.query(ChatRoom).filter(
            ChatRoom.room_id == chat_room_id,
            ChatRoom.room_type == "kefu"
        ).first()
        
        # 如果没有房间，创建新房间
        if not chat_room:
            # 分配客服（负载均衡或根据规则）
            from Models.login.models import Role
            platform_kefu_role = db.query(Role).filter(Role.code == "platform_kefu").first()
            assigned_kefu_id = None
            
            if platform_kefu_role:
                kefu_users = db.query(User).filter(User.role_id == platform_kefu_role.id).all()
                if kefu_users:
                    # 简单负载均衡：根据用户ID分配
                    assigned_kefu_id = kefu_users[hash(str(user_id_int)) % len(kefu_users)].id
            
            # 创建房间
            chat_room = ChatRoom(
                room_id=chat_room_id,
                user_id=user_id_int,
                kefu_id=assigned_kefu_id,
                room_type="kefu"
            )
            db.add(chat_room)
            db.commit()
            db.refresh(chat_room)
            logger.info(f"为用户 {user_id_int} 创建聊天房间 {chat_room_id}，分配客服: {assigned_kefu_id}")
        
        # 用户加入自己的聊天房间
        await room_manager.connect(websocket, chat_room_id, user_id_int, "user")
        
        # 获取用户信息
        user = db.query(User).filter(User.id == user_id_int).first()
        user_name = user.nickname or user.username or f"用户{user_id_int}" if user else f"用户{user_id_int}"
        
        # 发送连接成功消息
        await websocket.send_json({
            "type": "connected",
            "room_id": chat_room_id,
            "message": "连接成功，等待分配客服"
        })
        
        # 通知客服有新用户上线
        # 查找所有平台客服
        from Models.login.models import Role
        platform_kefu_role = db.query(Role).filter(Role.code == "platform_kefu").first()
        kefu_list = []
        
        if platform_kefu_role:
            kefu_users = db.query(User).filter(User.role_id == platform_kefu_role.id).all()
            kefu_list = [kefu.id for kefu in kefu_users]
        else:
            # 如果没有平台客服角色，查找普通客服
            kefu_role = db.query(Role).filter(Role.code == "kefu").first()
            if kefu_role:
                kefu_users = db.query(User).filter(User.role_id == kefu_role.id).all()
                kefu_list = [kefu.id for kefu in kefu_users]
        
        # 如果指定了客服，只通知该客服；否则通知所有客服
        if chat_room.kefu_id:
            kefu_list = [chat_room.kefu_id]
        
        # 通知每个客服（向客服通知房间推送用户信息）
        for kefu_id in kefu_list:
            kefu_notify_room = f"kefu_room_{kefu_id}"
            # 将用户添加到该客服的在线用户列表（Redis Set）
            online_users_key = f"kefu_online_users_{kefu_id}"
            redis_client.sadd(online_users_key, chat_room_id)
            
            # 发送新用户通知到客服通知房间
            await room_manager.send_to_room(
                kefu_notify_room,
                {
                    "type": "new_user",
                    "data": {
                        "user_id": user_id_int,
                        "room_id": chat_room_id,
                        "username": user_name,
                        "nickname": user.nickname if user else None
                    }
                }
            )
            logger.info(f"通知客服 {kefu_id} 有新用户 {user_id_int} 上线，房间: {chat_room_id}")
        
        # 三、加载历史消息
        history_messages = get_redis_messages(chat_room_id, limit=50)
        if history_messages:
            for msg in history_messages:
                await websocket.send_json({
                    "type": "message",
                    "data": msg
                })
        
        # 监听消息
        while True:
            try:
                data = await websocket.receive_text()
                message_data = json.loads(data)
                
                action = message_data.get("action")
                
                if action == "send_message":
                    # 四、双向消息通信 - 用户发送消息
                    content = message_data.get("content", "")
                    
                    if not content:
                        await websocket.send_json({
                            "type": "error",
                            "message": "content不能为空"
                        })
                        continue
                    
                    # 重新获取房间信息（可能已更新）
                    current_room = db.query(ChatRoom).filter(ChatRoom.room_id == chat_room_id).first()
                    if not current_room:
                        await websocket.send_json({
                            "type": "error",
                            "message": "房间不存在"
                        })
                        continue
                    
                    # 保存到数据库
                    try:
                        message = await save_message_to_db(db, chat_room_id, user_id_int, "user", content)
                        
                        if message:
                            message_dict = {
                                "id": message.id,
                                "room_id": str(message.room_id),
                                "sender_id": message.sender_id,
                                "sender_type": "user",
                                "content": message.content,
                                "created_at": message.created_at.isoformat()
                            }
                            
                            logger.info(f"用户 {user_id_int} 发送消息到房间 {chat_room_id}: {content[:50]}")
                            
                            # 保存到Redis（1天过期）
                            save_message_to_redis(chat_room_id, message_dict)
                            
                            # 更新房间更新时间
                            current_room.updated_at = datetime.now()
                            db.commit()
                            
                            # 先发送消息给发送者本人（确认发送成功）
                            await websocket.send_json({
                                "type": "message",
                                "data": message_dict
                            })
                            
                            # 发送到聊天房间内其他连接（如果有客服也在该房间）
                            sent_to_room = await room_manager.send_to_room(
                                chat_room_id,
                                {
                                    "type": "message",
                                    "data": message_dict
                                },
                                exclude_websocket=websocket
                            )
                            
                            # 同时发送到客服通知房间，确保客服能收到消息（即使还没加入聊天房间）
                            # 如果房间已分配客服，通知该客服；否则通知所有平台客服
                            total_kefu_notified = 0
                            if current_room.kefu_id:
                                # 通知分配的客服
                                kefu_notify_room = f"kefu_room_{current_room.kefu_id}"
                                sent_to_kefu = await room_manager.send_to_room(
                                    kefu_notify_room,
                                    {
                                        "type": "message",
                                        "data": message_dict
                                    }
                                )
                                total_kefu_notified = sent_to_kefu
                                logger.info(f"用户 {user_id_int} 发送的消息已推送到客服 {current_room.kefu_id} 的通知房间（{sent_to_kefu} 个连接）")
                            else:
                                # 如果房间未分配客服，通知所有平台客服
                                from Models.login.models import Role
                                platform_kefu_role = db.query(Role).filter(Role.code == "platform_kefu").first()
                                if platform_kefu_role:
                                    kefu_users = db.query(User).filter(User.role_id == platform_kefu_role.id).all()
                                    for kefu_user in kefu_users:
                                        kefu_notify_room = f"kefu_room_{kefu_user.id}"
                                        sent_to_kefu = await room_manager.send_to_room(
                                            kefu_notify_room,
                                            {
                                                "type": "message",
                                                "data": message_dict
                                            }
                                        )
                                        total_kefu_notified += sent_to_kefu
                                    logger.info(f"用户 {user_id_int} 发送的消息已推送到所有平台客服的通知房间（共 {total_kefu_notified} 个连接）")
                            
                            # 记录完整的推送结果
                            logger.info(f"用户 {user_id_int} 发送消息到房间 {chat_room_id}，聊天房间推送: {sent_to_room}，客服通知: {total_kefu_notified}: {content[:50]}")
                        else:
                            # 保存失败
                            logger.error(f"用户 {user_id_int} 发送消息失败: 保存到数据库返回None, room_id={chat_room_id}")
                            await websocket.send_json({
                                "type": "error",
                                "message": "消息保存失败，请稍后重试"
                            })
                    except Exception as e:
                        # 捕获异常
                        logger.error(f"用户 {user_id_int} 发送消息异常: {str(e)}, room_id={chat_room_id}", exc_info=True)
                        db.rollback()
                        await websocket.send_json({
                            "type": "error",
                            "message": f"消息发送失败: {str(e)}"
                        })
                
            except json.JSONDecodeError:
                await websocket.send_json({
                    "type": "error",
                    "message": "消息格式错误"
                })
            except Exception as e:
                logger.error(f"处理用户消息时出错: {e}", exc_info=True)
                await websocket.send_json({
                    "type": "error",
                    "message": f"处理消息时出错: {str(e)}"
                })
    
    except WebSocketDisconnect:
        # 五、断开连接
        # 用户断开连接时，从所有客服的在线用户列表中移除
        try:
            # 获取websocket对应的房间ID
            if websocket in room_manager.websocket_info:
                disconnected_room_id, disconnected_user_id, _ = room_manager.websocket_info[websocket]
                # 查询用户房间
                db = SessionLocal()
                disconnected_room = db.query(ChatRoom).filter(
                    ChatRoom.room_id == disconnected_room_id
                ).first()
                
                if disconnected_room:
                    redis_client = get_redis()
                    # 查找所有客服，从他们的在线用户列表中移除
                    from Models.login.models import Role
                    platform_kefu_role = db.query(Role).filter(Role.code == "platform_kefu").first()
                    kefu_list = []
                    if platform_kefu_role:
                        kefu_users = db.query(User).filter(User.role_id == platform_kefu_role.id).all()
                        kefu_list = [kefu.id for kefu in kefu_users]
                    else:
                        kefu_role = db.query(Role).filter(Role.code == "kefu").first()
                        if kefu_role:
                            kefu_users = db.query(User).filter(User.role_id == kefu_role.id).all()
                            kefu_list = [kefu.id for kefu in kefu_users]
                    
                    # 如果房间有分配的客服，也从该客服的列表中移除
                    if disconnected_room.kefu_id:
                        kefu_list.append(disconnected_room.kefu_id)
                    
                    # 从所有相关客服的在线用户列表中移除
                    for kefu_id in set(kefu_list):
                        online_users_key = f"kefu_online_users_{kefu_id}"
                        redis_client.srem(online_users_key, disconnected_room_id)
                    
                    db.close()
        except Exception as e:
            logger.error(f"清理用户在线状态失败: {e}")
        
        room_manager.disconnect(websocket)
        logger.info(f"用户 {user_id} WebSocket断开")
    except Exception as e:
        logger.error(f"用户WebSocket错误: {e}", exc_info=True)
        room_manager.disconnect(websocket)
    finally:
        if 'db' in locals():
            db.close()


# 向后兼容：旧路径也使用相同的实现
# FastAPI会自动处理路由参数，所以可以直接使用相同的函数
# 但由于路径参数格式不同（kefu3 vs kefu/3），需要分别处理

# 旧路径：/ws/kefu/{kefu_id} - 参数格式为纯数字或带kefu前缀
# 新路径：/room/kefu{kefu_id}/ - 参数格式为kefu3或3
# 由于参数解析逻辑相同，可以复用主函数

@router.websocket("/ws/kefu/{kefu_id}")
async def kefu_websocket_legacy(websocket: WebSocket, kefu_id: str):
    """
    客服WebSocket连接 - 向后兼容路径 /ws/kefu/{kefu_id}
    调用主函数处理
    """
    await kefu_websocket(websocket, kefu_id)


@router.websocket("/ws/user/{user_id}")
async def user_websocket_legacy(websocket: WebSocket, user_id: str):
    """
    用户WebSocket连接 - 向后兼容路径 /ws/user/{user_id}
    调用主函数处理
    """
    await user_websocket(websocket, user_id)


@router.websocket("/ws/merchant/{merchant_id}")
async def merchant_websocket(websocket: WebSocket, merchant_id: str):
    """商家WebSocket连接"""
    try:
        # 商家房间号格式：merchant_{merchant_id}
        merchant_room_id = f"merchant{merchant_id}"
        # 如果merchant_id已经是纯数字，直接使用；否则提取数字
        merchant_id_int = int(merchant_id) if merchant_id.isdigit() else int(merchant_id.replace('merchant', '').replace('user', '').replace('kefu', ''))
        
        await room_manager.connect(websocket, merchant_room_id, merchant_id_int, "merchant")
        db = SessionLocal()
        
        # 发送连接成功消息
        await websocket.send_json({
            "type": "connected",
            "room_id": merchant_room_id,
            "message": "商家连接成功"
        })
        
        # 获取该商家的所有房间（待服务用户列表）
        merchant_rooms = db.query(ChatRoom).filter(
            ChatRoom.merchant_id == merchant_id_int,
            ChatRoom.room_type == "merchant",
            ChatRoom.is_active == True
        ).all()
        
        # 发送用户列表给商家
        user_list = []
        for room in merchant_rooms:
            user = db.query(User).filter(User.id == room.user_id).first()
            if user:
                # 获取最后一条消息
                last_message = db.query(ChatMessage).filter(
                    ChatMessage.room_id == room.room_id
                ).order_by(ChatMessage.created_at.desc()).first()
                
                user_info = {
                    "id": room.user_id,
                    "name": user.nickname or user.username or f"用户{room.user_id}",
                    "room_id": room.room_id,
                    "last_message": None
                }
                
                if last_message:
                    user_info["last_message"] = {
                        "content": last_message.content,
                        "created_at": last_message.created_at.isoformat()
                    }
                
                user_list.append(user_info)
        
        # 发送用户列表
        await websocket.send_json({
            "type": "user_list",
            "data": user_list
        })
        
        # 监听消息
        while True:
            try:
                data = await websocket.receive_text()
                message_data = json.loads(data)
                
                # 处理来自房间管理器的消息（如新用户通知）
                # 注意：新用户通知是通过room_manager.send_to_room发送到商家房间的
                # 但由于FastAPI WebSocket的特性，我们需要在商家连接时处理
                # 实际上，新用户通知会在用户连接时发送，但商家需要通过轮询或重新获取列表
                
                action = message_data.get("action")
                
                if action == "send_message":
                    # 发送消息
                    content = message_data.get("content", "")
                    target_room_id = message_data.get("room_id")
                    
                    if not target_room_id or not content:
                        await websocket.send_json({
                            "type": "error",
                            "message": "room_id和content不能为空"
                        })
                        continue
                    
                    # 验证房间是否属于该商家
                    target_room = db.query(ChatRoom).filter(ChatRoom.room_id == target_room_id).first()
                    if not target_room:
                        await websocket.send_json({
                            "type": "error",
                            "message": "房间不存在"
                        })
                        continue
                    
                    if target_room.merchant_id != merchant_id_int:
                        await websocket.send_json({
                            "type": "error",
                            "message": "无权访问该房间"
                        })
                        continue
                    
                    # 保存到数据库
                    message = await save_message_to_db(db, target_room_id, merchant_id_int, "merchant", content)
                    
                    if message:
                        message_dict = {
                            "id": message.id,
                            "room_id": message.room_id,
                            "sender_id": message.sender_id,
                            "sender_type": "merchant",
                            "content": message.content,
                            "created_at": message.created_at.isoformat()
                        }
                        
                        # 保存到Redis（30天过期）
                        save_message_to_redis(target_room_id, message_dict)
                        
                        # 更新房间更新时间
                        target_room.updated_at = datetime.now()
                        db.commit()
                        
                        # 先发送消息给发送者本人（确认发送成功）
                        await websocket.send_json({
                            "type": "message",
                            "data": message_dict
                        })
                        
                        # 发送到用户聊天房间（用户可以看到）
                        sent_count = await room_manager.send_to_room(
                            target_room_id,
                            {
                                "type": "message",
                                "data": message_dict
                            },
                            exclude_websocket=websocket
                        )
                        
                        # 记录推送结果
                        if sent_count > 0:
                            logger.info(f"商家 {merchant_id_int} 发送消息到房间 {target_room_id}，已推送给 {sent_count} 个用户: {content[:50]}")
                        else:
                            # 用户可能不在线，但消息已保存到数据库和Redis，用户重连时会加载
                            logger.info(f"商家 {merchant_id_int} 发送消息到房间 {target_room_id}，用户当前不在线，消息已保存: {content[:50]}")
                
                elif action == "join_user_room":
                    # 加入用户房间
                    user_room_id = message_data.get("room_id")
                    if user_room_id:
                        # 验证房间是否属于该商家
                        target_room = db.query(ChatRoom).filter(ChatRoom.room_id == user_room_id).first()
                        if target_room and target_room.merchant_id == merchant_id_int:
                            await room_manager.connect(websocket, user_room_id, merchant_id_int, "merchant")
                            
                            # 加载历史消息（从Redis）
                            history_messages = get_redis_messages(user_room_id, limit=50)
                            if history_messages:
                                for msg in history_messages:
                                    await websocket.send_json({
                                        "type": "message",
                                        "data": msg
                                    })
                            
                            await websocket.send_json({
                                "type": "joined_room",
                                "room_id": user_room_id,
                                "message": f"已加入房间 {user_room_id}"
                            })
                        else:
                            await websocket.send_json({
                                "type": "error",
                                "message": "无权访问该房间"
                            })
                
                elif action == "get_user_list":
                    # 重新获取用户列表
                    merchant_rooms = db.query(ChatRoom).filter(
                        ChatRoom.merchant_id == merchant_id_int,
                        ChatRoom.room_type == "merchant",
                        ChatRoom.is_active == True
                    ).all()
                    
                    user_list = []
                    for room in merchant_rooms:
                        user = db.query(User).filter(User.id == room.user_id).first()
                        if user:
                            # 获取最后一条消息
                            last_message = db.query(ChatMessage).filter(
                                ChatMessage.room_id == room.room_id
                            ).order_by(ChatMessage.created_at.desc()).first()
                            
                            user_info = {
                                "id": room.user_id,
                                "name": user.nickname or user.username or f"用户{room.user_id}",
                                "room_id": room.room_id,
                                "last_message": None
                            }
                            
                            if last_message:
                                user_info["last_message"] = {
                                    "content": last_message.content,
                                    "created_at": last_message.created_at.isoformat()
                                }
                            
                            user_list.append(user_info)
                    
                    await websocket.send_json({
                        "type": "user_list",
                        "data": user_list
                    })
                
            except json.JSONDecodeError:
                await websocket.send_json({
                    "type": "error",
                    "message": "消息格式错误"
                })
            except Exception as e:
                logger.error(f"处理商家消息时出错: {e}")
                await websocket.send_json({
                    "type": "error",
                    "message": f"处理消息时出错: {str(e)}"
                })
    
    except WebSocketDisconnect:
        room_manager.disconnect(websocket)
        logger.info(f"商家 {merchant_id} WebSocket断开")
    except Exception as e:
        logger.error(f"商家WebSocket错误: {e}", exc_info=True)
        room_manager.disconnect(websocket)
    finally:
        if 'db' in locals():
            db.close()

